﻿/*
 * HardwareCommunication.cs
 * Written by: Shawn Gilleland & Miguel Sanchez (he'll need to add his parts)
 * 
 * This is an implementation class that interacts directly with the
 * hardware for Supply Fetch.  This is the implementation class
 * for the Hardware Communication Gateway Subsystem that uses
 * the Bridge Pattern.
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO.Ports;
using System.Management;
using System.Windows.Forms;
using System.Threading;
using System.Security.Permissions;
using CPLayer;
using Supply_Fetch;

namespace IndicationLayer
{
    class ArduinoCommunication:HardwareCommunicationImpl
    {
        //Define class constants
        public const int RED = 255;
        public const int BLUE = 160;
        public const int GREEN = 75;
        public const int MAGENTA = 235;

        //Create class attributes
        private SerialPort serialPort1; //= new SerialPort();
        int address;
        int hue;
        bool is_connected=false;
        

        /// <summary>
        /// Constructor: Initilize Communication Port and default drawer attributes
        /// </summary>
        public ArduinoCommunication()
        {
            //set default address/hue
            this.address = 1;
            this.hue = 255;
        }

        /// <summary>
        /// Connect to HCG
        /// </summary>
        public void ConnectToSerialPort(){
            try
            {
                serialPort1 = new SerialPort();
                serialPort1.PortName = AutodetectArduinoPort();
                serialPort1.BaudRate = 9600;
                serialPort1.Open();
                is_connected = true;
            }
            catch (Exception e)
            {
                MessageBox.Show("The Supply Fetch Controller is not connected,\nplease connect the necessary system device\nin order to perform your request.\n\n--Details--\n" + e.Message);
                is_connected = false;
            }

            if (is_connected)
            {
                //configure fade speed after connecting: 0(slow fade) to 255(fast fade)
                SendSerial("f 0 100");
                CommunicationWait(100);
            }
        }

        public void CloseSerialPort()
        {
            try
            {
                serialPort1 = new SerialPort();
                serialPort1.PortName = AutodetectArduinoPort();
                serialPort1.BaudRate = 9600;
                serialPort1.Open();
                this.is_connected = true;
            }
            catch (Exception e)
            {
                this.is_connected = false;
            }
        }

        /// <summary>
        /// Check if there is successful communication between the port and the Arduino device.
        /// </summary>
        /// <returns></returns>
        public bool SerialPortIsOpen()
        {
            return is_connected;
        }

        /// <summary>
        /// Find the port in which the Arduino is connected
        /// http://stackoverflow.com/questions/3293889/how-to-auto-detect-arduino-com-port
        /// </summary>
        /// <returns></returns>
        private string AutodetectArduinoPort()
        {
            ManagementScope connectionScope = new ManagementScope();
            SelectQuery serialQuery = new SelectQuery("SELECT * FROM Win32_SerialPort");
            ManagementObjectSearcher searcher = new ManagementObjectSearcher(connectionScope, serialQuery);

            try
            {
                foreach (ManagementObject item in searcher.Get())
                {
                    string desc = item["Description"].ToString();
                    string deviceId = item["DeviceID"].ToString();

                    //if (desc.Contains("Arduino"))
                    if(desc.Contains("Communications Port"))
                    {
                        return deviceId;
                    }
                }
            }
            catch (ManagementException e)
            {
                /* Do Nothing */
            }
            return null;
        }

        /// <summary>
        /// DisposeHCG() calls Dispose(true)
        /// http://msdn.microsoft.com/en-us/library/fs2xkftw%28VS.80%29.aspx
        /// http://stackoverflow.com/questions/12657479/access-to-the-port-com5-is-denied
        /// </summary>
        public void DisposeSerialPort()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
            is_connected = false;
        }

        /// <summary>
        /// The bulk of the clean-up code is implemented in Dispose(bool)
        /// http://msdn.microsoft.com/en-us/library/cekaawx6(v=vs.80).aspx
        /// Notes to Inheritors Dispose can be called multiple times by other objects. 
        /// When overriding Dispose, be careful not to reference objects that have been previously disposed of in an earlier call to Dispose. 
        /// For more information about how to implement Dispose, see Implementing a Dispose Method. 
        /// For more information about Dispose and Finalize, see Cleaning Up Unmanaged Resources and Overriding the Finalize Method.
        /// </summary>
        /// <param name="disposing"></param>
        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                // free managed resources
                if (serialPort1 != null)
                {
                    serialPort1.Dispose();
                    serialPort1 = null;
                }
            }
            // free native resources if there are any.
        }

        /// <summary>
        /// Closes communication serial port when called
        /// </summary>
        public void CloseCommunicationPort()
        {
           if (serialPort1.IsOpen) serialPort1.Close();
        }

        /// <summary>
        /// Sends serial communication string
        /// </summary>
        /// <param name="str"></param>
        private void SendSerial(String str)
        {
            serialPort1.WriteLine(str);
        }

        public void ActivateLED(int LED_ID)
        {
            SendSerial("h "+LED_ID+" 255");
        }

        public void DeactivateLED(int LED_ID)
        {
            SendSerial("O " + LED_ID);
        }

        /// <summary>
        /// Activates all drawers.
        /// Note: Implementation may change if not enough current is able to be distributed to all LED's
        /// </summary>
        public void ActivateAllDrawers()
        {
            SendSerial("h 0 255");
        }

        /// <summary>
        /// Activates SET of drawers in which the item being searched for is located.
        /// Iterates through list and sends command one at a time with a brief delay after 
        /// sending to avoid overflowing buffer in Arduino script.
        /// </summary>
        /// <param name="drawers"></param>
        public void ActivateDrawers(List<Drawer> drawers)
        { 
            string command;//command must be of the form "h <address> <hue>" in order for the arduino to understand 
            int dalay_ms=100; //delay between sending messeges via serial port in milliseconds

            foreach (Drawer drawer in drawers){
                address=drawer.GetColumn(); 
                hue=GetHue(drawer.GetRow());//send row to color map process 
                command = "h " + address + " " + hue; 
                 
                //send hardware command 
                SendSerial(command); 
 
                //wait before sending next drawer command if multiple locations 
                CommunicationWait(dalay_ms); 
           } 
        }

        /// <summary>
        /// Map item row to a color(hue) that the controller can understand 
        /// </summary>
        /// <param name="row"></param>
        /// <returns></returns>
        private int GetHue(int row)
        {
            switch (row)
            {
                case 1: return RED;
                case 2: return BLUE;
                case 3: return GREEN;
                case 4: return MAGENTA;
                //row of 0 would “broadcast” the color red to every  
                //drawer (in the case all drawers need to be tested) 
                default: return RED;
            }
        }

        /// <summary>
        /// Turn off all drawers
        /// </summary>
        public void DeactivateAllDrawers()
        {
            SendSerial("o 0");
            CommunicationWait(100);
            SendSerial("O 0");//send another off just in case :)
        }

        /// <summary>
        /// Hold communication process before sending another 
        /// command to Arduino Controller
        /// </summary>
        /// <param name="t"></param>
        public void CommunicationWait(int t)
        {
            //Start Delay
            DateTime timeAtStart = DateTime.Now;
            int maxWaitTime = t;
            while (true)
            {
                if ((DateTime.Now - timeAtStart).TotalMilliseconds >= maxWaitTime)
                {
                    break;
                }
            }
        }
    }
}
